package com.redxiii.spike.engine;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.redxiii.util.cli.CmdLineIO;


/**
 * @author Daniel Filgueiras
 * 23/04/2011
 */
public class CoreMain {
	
	/*
	 * Static Scope
	 */
	private static LinkedBlockingQueue<String> engines = new LinkedBlockingQueue<String>();
	private static AtomicBoolean keepAlive = new AtomicBoolean(true);
	
	public static final void coreShutdown() {
		keepAlive.set(Boolean.FALSE);
	}
	
	public static final boolean isAlive() {
		return keepAlive.get();
	}
	
	public static void main(String[] args) {
		
		Logger logger = LoggerFactory.getLogger(CoreMain.class);
		ExecutorService poolService;
		ThreadPoolExecutor executor = new ThreadPoolExecutor(
				1,1000,60,TimeUnit.SECONDS,
				new SynchronousQueue<Runnable>(),
				new EngineThreadFactory("CoreEngine"),
				new ThreadPoolExecutor.CallerRunsPolicy());
		
		poolService = Executors.unconfigurableExecutorService(executor);
		
		if (args == null || args.length == 0){
			CmdLineIO io = new CmdLineIO();
			io.setEscOnEmpty(false);
			args = new String[]{io.getString("ID")};
		}
			
		
		for (String id : args) {
			engines.add(id);
		}
		
		List<Future<?>> futures = new ArrayList<Future<?>>();
		while (keepAlive.get()) {
			try {
				String id = engines.poll(60,TimeUnit.SECONDS);
				if (id != null) {
					logger.info("Starting new engine {}", id);
					Future<?> future;
					try {
						future = poolService.submit(new EngineFutureTask(new CoreEngine(id)));
						futures.add(future);
					} catch (Exception e) {
						logger.warn("Could not start engine {}", id, e);
					}
					Thread.sleep(500);
				}
				
				logger.trace("Started {} engines", futures.size());
				
				boolean allDone = true;
				for (Future<?> future : futures) {
					allDone &= future.isDone();
				}
				if (allDone) {
					logger.warn("All communication services are turned off");
					coreShutdown();
				}
				
			} catch (InterruptedException e) {
				logger.warn("Take interrupted",e);
				coreShutdown();
			}
		}
		
		logger.info("Cancel Engines");
		for (Future<?> future : futures) {
			future.cancel(true);
		}
		
		logger.info("Shutting down...");
		poolService.shutdownNow();
	
		logger.info("Exiting program...");
	}
}


class EngineThreadFactory implements ThreadFactory {

    private static AtomicInteger threadNumber = new AtomicInteger(1);
    private final String namePrefix;

    /**
     * Constructor accepting the prefix of the threads that will be created by this {@link ThreadFactory}
     * 
     * @param namePrefix
     *            Prefix for names of threads
     */
    public EngineThreadFactory(String namePrefix) {
        this.namePrefix = namePrefix;
    }

    /**
     * Returns a new thread using a name as specified by this factory {@inheritDoc}
     */
    public Thread newThread(Runnable runnable) {
        return new Thread(runnable, namePrefix + "-" + threadNumber.getAndIncrement());
    }

}

class EngineFutureTask extends FutureTask<Object> {
	
	private final Logger logger = LoggerFactory.getLogger(getClass());
	
	public EngineFutureTask(CoreEngine engine)	{
	    super(engine, null);
	}

	protected void setException(Throwable throwable) {
	    super.setException(throwable);
	    logger.error("Error running engine", throwable);
	}
}